#include "simplelogger.h"
#include <QThread>
#include <QDir>
#include <QDebug>
#include <QDateTime>

SimpleLogger * SimpleLogger::instance = nullptr;
QMutex SimpleLogger::instanceLock;

SimpleLogger::SimpleLogger(QObject *parent) : QObject(parent)
{    
    connect(&timer, SIGNAL(timeout()), this, SLOT(onTimerTimeout()));
    timer.setInterval(1000);
    timer.start();
}

SimpleLogger * SimpleLogger::getInstance()
{
    if(!instance){
        instanceLock.lock();
        if(!instance){
            instance = new SimpleLogger();
        }
        instanceLock.unlock();
    }
    return instance;
}


qint64 SimpleLogger::getMaxFileSize()
{
    return maxFileSize;
}

void SimpleLogger::setMaxFileSize(qint64 value)
{
    maxFileSize = value;
}

qint64 SimpleLogger::getMaxBackUp()
{
    return maxBackUp;
}

void SimpleLogger::setMaxBackUp(qint64 value)
{
    maxBackUp = value;
}

QString SimpleLogger::getLogFileName()
{
    return logFileName;
}

void SimpleLogger::setLogFileName(const QString &value)
{
    logFileName = value;
}

QString SimpleLogger::getLogFilePath()
{
    return logFilePath;
}

void SimpleLogger::setLogFilePath(const QString &value)
{
    logFilePath = value;
}

void SimpleLogger::initRollingFileLog()
{
    qDebug() << "logLock locking"; logLock.lock(); qDebug() << "logLock locked";
    if(logFilePath.startsWith("./")){
        logFilePath = QDir::currentPath() +"/" + logFilePath;
    }
    QDir dir(logFilePath);
    if(!dir.exists()){
        dir.mkdir(logFilePath);
        qDebug() << "create log file path: " << logFilePath;
    }
    // simplelogger.log.0 simplelogger.log.1 simplelogger.log.2 simplelogger.log.3
    QString currentLogFileAbsoluteFilePath = QString("%1/%2").arg(logFilePath).arg(logFileName);    
    qDebug() << "currentLogFileSize: " << currentLogfileSize <<", maxFileSize: " << maxFileSize;
    if(currentLogfileSize >= maxFileSize){
        currentlogFile->flush();
        currentlogFile->close();
        currentlogFile->deleteLater();
        currentlogFile = nullptr;
        //mv files, .2->.3 , .1 -> .2, new .log
        for(int i = maxBackUp; i>0; i--){
            QFile dest(QString("%1.%2").arg(currentLogFileAbsoluteFilePath).arg(i));
            if(dest.exists()){
                dest.remove();
                qInfo() << "remove " << dest.fileName();
            }
            QFile src(QString("%1.%2").arg(currentLogFileAbsoluteFilePath).arg(i - 1));
            if(src.exists()){
                qInfo() << "rename " << src.fileName() << " to " << dest.fileName();
                src.rename(dest.fileName());
            }
        }
        qInfo() << "init new logFile: ";        
    }
    initLogFile(currentLogFileAbsoluteFilePath+".0");
    qDebug() << "logLock unlocking"; logLock.unlock(); qDebug() << "logLock unlocked";
}



void SimpleLogger::initLogFile(QString currentLogFileAbsoluteFilePath)
{
    qDebug() << "checking current log file: " << currentLogFileAbsoluteFilePath;
    currentlogFile = new QFile(currentLogFileAbsoluteFilePath);    
    bool opened =  currentlogFile->open(QIODevice::Append | QIODevice::Text);
    if(!opened){
        qWarning() << "cannot open log file " << currentLogFileAbsoluteFilePath;
        currentlogFile->deleteLater();
        currentlogFile = nullptr;
        currentLogfileSize = 0;
        return;
    }
    currentLogfileSize = currentlogFile->size();
}

void SimpleLogger::writeLogFile(QString finalMessage)
{
    if(!currentlogFile){
        qWarning() << finalMessage;
        return;
    }
    if(currentLogfileSize < maxFileSize){
        logLock.lock();
        currentLogfileSize += finalMessage.toUtf8().size();
        qint64 w = currentlogFile->write(finalMessage.toUtf8().data());
        //        currentlogFile->flush();
        logLock.unlock();
    }
    else{
        initRollingFileLog();
        writeLogFile(finalMessage);
    }
}

void SimpleLogger::onTimerTimeout()
{
    if(currentlogFile){
        logLock.lock();
        currentlogFile->flush();
        logLock.unlock();
    }
}


void SimpleLogger::consoleOutputMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    QByteArray localMsg = msg.toLocal8Bit();
    fprintf(
                stderr,
                "%s[%lx]%s: %s (%s:%u, %s)\n",
                QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz").toUtf8().data(),
                (qint64)QThread::currentThreadId(),
                type == QtMsgType::QtDebugMsg ? "DEBUG" : type == QtMsgType::QtInfoMsg ? "INFO" : type == QtMsgType::QtWarningMsg ? "WARN" : type == QtMsgType::QtCriticalMsg ? "CRITICAL" : type == QtMsgType::QtFatalMsg ? "FATAL" : "UNKNOWN",
                localMsg.constData(),
                context.file,
                context.line,
                context.function
                );
}

void SimpleLogger::rollingFileOutputMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    QByteArray localMsg = msg.toLocal8Bit();
    QString finalMessage = QString("%1[%2]%3: %4 (%5:%6, %7)\n")
            .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz"))
            .arg((qint64)QThread::currentThreadId())
            .arg(type == QtMsgType::QtDebugMsg ? "DEBUG" : type == QtMsgType::QtInfoMsg ? "INFO" : type == QtMsgType::QtWarningMsg ? "WARN" : type == QtMsgType::QtCriticalMsg ? "CRITICAL" : type == QtMsgType::QtFatalMsg ? "FATAL" : "UNKNOWN")
            .arg(localMsg.constData())
            .arg(context.file)
            .arg(context.line)
            .arg(context.function);
    instance->writeLogFile(finalMessage);
}
